#include "LobbyState.h"
#include "ServerMenuState.h"
#include "ServerGamePlayState.h"
#include "ClientConnectNetworkEvent.h"
#include "ClientReadyToPlayNetworkEvent.h"
#include "UpdatePlayerNetworkEvent.h"
#include "GameStartNetworkEvent.h"
#include "Players.h"
#include "Player.h"
#include "Configuration.h"
#include "CountDownState.h"

#include <sstream>

LobbyState::LobbyState(unsigned int clientsToWaitFor, double wellSizeFactor)
        : MenuState("Lobby"), clientsToWaitFor(clientsToWaitFor), info(NULL), wellSizeFactor(wellSizeFactor),
                isReady(false)
{
}

void LobbyState::addMenuLines()
{
    addLine("1. Start Game");
    addLine("2. Ready To Play");
    addLine("ESC. Back");
    addLine("");
    info = addLine("");
    updateText();
    addPlayerNameLines();
}

void LobbyState::addPlayerNameLines()
{
    for (unsigned int i = 0; i < clientsToWaitFor; i++)
    {
        Text* text = addLine("Not connected");
        playerTexts.push_back(text);
    }
}

void LobbyState::init()
{
    MenuState::init();
    players = PlayersPtr(new Players());
    server = ServerPtr(new Server());
    server->start();
    client = ClientPtr(new Client(true));
    client->start();
    ClientConnectNetworkEvent event((Configuration::get(Configuration::CLIENT_NAME)).c_str());
    client->send(event);
}

void LobbyState::updatePlayerTexts()
{
    int playerIndex = 0;
    for (std::vector<Text*>::iterator it = playerTexts.begin(); it != playerTexts.end(); it++)
    {
        (*it)->setColor(Color(255, 255, 255));
        (*it)->setText("Not connected");
    }
    PlayerIds playerIds = players->getPlayerIds();
    for (PlayerIds::iterator it = playerIds.begin(); it != playerIds.end(); it++)
    {
        playerTexts[playerIndex]->setColor(players->getPlayer(*it)->getColor());
        playerTexts[playerIndex]->setText(players->getPlayer(*it)->getName());
        playerIndex++;
    }
}

void LobbyState::updateText()
{
    std::ostringstream infoString;
    infoString << "Connected: " << players->getNumberOfPlayers() << ", Ready: " << players->getNumberOfReadyPlayers();

    updatePlayerTexts();
    info->setText(infoString.str());
}

GameState* LobbyState::performMenuAction1()
{
    if (okToStartGame())
    {
        int wellWidth = (10 + (players->getNumberOfPlayers() - 1) * 5) * wellSizeFactor;
        GameStartNetworkEvent event(wellWidth);
        server->sendToClients(event);
    }
    return this;
}

bool LobbyState::okToStartGame()
{
    return (players->getNumberOfReadyPlayers() == clientsToWaitFor);
}

GameState* LobbyState::performMenuAction2()
{
    if (!isReady)
    {
        ClientReadyToPlayNetworkEvent event;
        client->send(event);
        isReady = true;
    }
    return this;
}

GameState* LobbyState::performMenuActionEscape()
{
    client->quit();
    server->quit();
    return goToNextState(new ServerMenuState());
}

GameState* LobbyState::handleClientConnectEvent(int playerId, const std::string& playerName)
{
    players->add(playerId, playerName);
    updateText();
    sendPlayerUpdates();
    return this;
}

GameState* LobbyState::handleClientDisconnectedEvent(int playerId)
{
    players->remove(playerId);
    updateText();
    server->removeClient(playerId);
    sendPlayerUpdates();
    return this;
}

void LobbyState::sendPlayerUpdates()
{
    UpdatePlayerNetworkEvent resetEvent(UpdatePlayerNetworkEvent::RESET, 0, "", ColorBlack());
    server->sendToClients(resetEvent);

    PlayerIds playerIds = players->getPlayerIds();
    for (PlayerIds::iterator it = playerIds.begin(); it != playerIds.end(); it++)
    {
        UpdatePlayerNetworkEvent addEvent(UpdatePlayerNetworkEvent::ADD, *it,
                players->getPlayer(*it)->getName().c_str(), players->getPlayerColor(*it));
        server->sendToClients(addEvent);
    }
}

GameState* LobbyState::handleServerGameStartEvent(unsigned int widthOfWell)
{
    GameState* nextState = new ServerGamePlayState(client, server, players, widthOfWell);
    return goToNextState(new CountDownState(nextState));
}

GameState* LobbyState::handleClientReadyToPlayEvent(int playerId)
{
    players->getPlayer(playerId)->setReady();
    updateText();
    return this;
}

GameState* LobbyState::handleEvent(const Event& event)
{
    event.visitLobbyState(this);
    return MenuState::handleEvent(event);
}

LobbyState::~LobbyState()
{
}

